/**
 * Copyright (c) 2006 Amgad S Youssef
 * All Rights Reserved.
 * Licensed under the Eclipse Public License - v 1.0
 * For more information see http://www.eclipse.org/legal/epl-v10.html
 */
package org.eclipsejcde.jcbp;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.util.Properties;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeHierarchy;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipsejcde.core.JCDECore;

/**
 * @author Amgad S Youssef
 * 
 */
public class JCBPCore {

	public static final String NATURE_ID = "org.eclipsejcde.jcbp.nature";

	public static final String BUILDER_ID = "org.eclipsejcde.jcbp.builder";

	public static final String MARKER_TYPE = "org.eclipsejcde.jcbp.problem";

	public static final String PROJECT_PROPERTIES_FILE_NAME = ".jcbpproject";

	public static final String SOSFRAMEWORK_PACKAGE_NAME = "sosframework.package.name";

	public static IStatus setProjectProperty(IJavaProject javaProject, String name, String value) {
		if (javaProject == null) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, "javaProject is null", null);
		}
		if (name == null || name.equals("")) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, "name is null or empty", null);
		}
		if (value == null) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, "value is null", null);
		}
		try {
			Properties projectProperties = new Properties();
			File projectPropertiesFile = javaProject.getResource().getLocation().append(File.separator + PROJECT_PROPERTIES_FILE_NAME).toFile();
			if (projectPropertiesFile.exists()) {
				projectProperties.loadFromXML(new FileInputStream(projectPropertiesFile));
			} else {
				projectPropertiesFile.createNewFile();
			}
			projectProperties.setProperty(name, value);
			projectProperties.storeToXML(new FileOutputStream(projectPropertiesFile), "Java Card Best Practices project properties");
			return new Status(IStatus.OK, Activator.PLUGIN_ID, IStatus.OK, "\"" + name + "\" set successfully.", null);
		} catch (Exception e) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, e.getMessage(), e);
		}
	}

	public static IStatus getProjectProperty(IJavaProject javaProject, String name) {
		if (javaProject == null) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, "javaProject is null", null);
		}
		if (name == null || name.equals("")) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, "name is null or empty", null);
		}
		try {
			File projectPropertiesFile = javaProject.getResource().getLocation().append(File.separator + PROJECT_PROPERTIES_FILE_NAME).toFile();
			if (!projectPropertiesFile.exists()) {
				return new Status(IStatus.WARNING, Activator.PLUGIN_ID, IStatus.OK, "\"" + name + "\" property does not exist.", null);
			}
			Properties projectProperties = new Properties();
			projectProperties.loadFromXML(new FileInputStream(projectPropertiesFile));
			String propertyValue = projectProperties.getProperty(name);
			return new Status(IStatus.OK, Activator.PLUGIN_ID, IStatus.OK, propertyValue, null);
		} catch (Exception e) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, e.getMessage(), e);
		}
	}

	public static boolean isSOSApplet(ICompilationUnit compilationUnit) throws JavaModelException {
		if (compilationUnit == null || !compilationUnit.exists()) {
			return false;
		}
		IType type = compilationUnit.findPrimaryType();
		if (type == null) {
			return false;
		}
		if (!JCDECore.isJavacardApplet(compilationUnit)) {
			return false;
		}
		IStatus status = getProjectProperty(compilationUnit.getJavaProject(), SOSFRAMEWORK_PACKAGE_NAME);
		if (status.getSeverity() != IStatus.OK) {
			return false;
		}
		String sosFrameworkPackageName = status.getMessage();
		ITypeHierarchy typeHierarchy = type.newTypeHierarchy(null);
		IType[] superTypes = typeHierarchy.getAllSuperclasses(type);
		for (int i = 0; i < superTypes.length; i++) {
			if (superTypes[i].getFullyQualifiedName().equals(sosFrameworkPackageName + ".SOSApplet")) {
				return true;
			}
		}
		return false;
	}

	public static boolean isSIProxy(ICompilationUnit compilationUnit) throws JavaModelException {
		if (compilationUnit == null || !compilationUnit.exists()) {
			return false;
		}
		IType type = compilationUnit.findPrimaryType();
		if (type == null) {
			return false;
		}
		if (!type.isClass()) {
			return false;
		}
		IStatus status = getProjectProperty(compilationUnit.getJavaProject(), SOSFRAMEWORK_PACKAGE_NAME);
		if (status.getSeverity() != IStatus.OK) {
			return false;
		}
		String sosFrameworkPackageName = status.getMessage();

		ITypeHierarchy typeHierarchy = type.newTypeHierarchy(null);
		IType[] superTypes = typeHierarchy.getAllSuperclasses(type);
		for (int i = 0; i < superTypes.length; i++) {
			if (superTypes[i].getFullyQualifiedName().equals(sosFrameworkPackageName + ".SIProxy")) {
				return true;
			}
		}
		return false;
	}

	public static boolean isSOSFrameworkComponent(ICompilationUnit compilationUnit) throws JavaModelException {
		if (compilationUnit == null || !compilationUnit.exists()) {
			return false;
		}
		IType type = compilationUnit.findPrimaryType();
		if (type == null) {
			return false;
		}
		IStatus status = getProjectProperty(compilationUnit.getJavaProject(), SOSFRAMEWORK_PACKAGE_NAME);
		if (status.getSeverity() != IStatus.OK) {
			return false;
		}
		String sosFrameworkPackageName = status.getMessage();

		if (type.getFullyQualifiedName().startsWith(sosFrameworkPackageName)) {
			return true;
		}

		return false;
	}

	public static void extractSOSFrameworkJar(String destinationLocation) throws IOException {
		URL url = FileLocator.toFileURL(FileLocator.find(Activator.getDefault().getBundle(), new Path("/META-INF/sosframework.jar"), null));
		String jarFileLocation = url.getFile();
		byte[] buf = new byte[1024];
		ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(jarFileLocation));
		ZipEntry zipEntry = zipInputStream.getNextEntry();
		while (zipEntry != null) {
			// for each entry to be extracted
			if (zipEntry.isDirectory()) {
				File newDirectory = new File(destinationLocation + File.separator + zipEntry.getName());
				if (!newDirectory.exists()) {
					newDirectory.mkdirs();
				}
			} else {
				File file = new File(destinationLocation + File.separator + zipEntry.getName());
				if (!file.getParentFile().exists()) {
					file.getParentFile().mkdirs();
				}
				if (!file.exists()) {
					FileOutputStream fileOutputStream = new FileOutputStream(file);
					int n = -1;
					while ((n = zipInputStream.read(buf, 0, 1024)) > -1) {
						fileOutputStream.write(buf, 0, n);
					}
					fileOutputStream.close();
					zipInputStream.closeEntry();
				}
			}
			zipEntry = zipInputStream.getNextEntry();

		}// while
		zipInputStream.close();
	}

	public static boolean sosFrameworkExists(String destinationLocation) throws IOException {
		URL url = FileLocator.toFileURL(FileLocator.find(Activator.getDefault().getBundle(), new Path("/META-INF/sosframework.jar"), null));
		String jarFileLocation = url.getFile();
		ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(jarFileLocation));
		ZipEntry zipEntry = zipInputStream.getNextEntry();
		while (zipEntry != null) {
			// for each entry to be checked
			File file = new File(destinationLocation + File.separator + zipEntry.getName());
			if (!file.exists()) {
				return false;
			}
			zipEntry = zipInputStream.getNextEntry();
		}// while
		zipInputStream.close();
		return true;
	}

	public static IStatus toggleJCBPProjectNature(IJavaProject javaProject) {
		if (isJCBPProjectNatureEnabled(javaProject)) {
			return disableJCBPProjectNature(javaProject);
		} else {
			return enableJCBPProjectNature(javaProject);
		}
	}

	public static boolean isJCBPProjectNatureEnabled(IJavaProject javaProject) {
		if (javaProject == null) {
			return false;
		}
		try {
			IProjectDescription description = javaProject.getProject().getDescription();
			String[] natures = description.getNatureIds();
			for (int i = 0; i < natures.length; ++i) {
				if (NATURE_ID.equals(natures[i])) {
					return true;
				}
			}
			return false;
		} catch (Exception e) {
			return false;
		}
	}

	public static IStatus enableJCBPProjectNature(IJavaProject javaProject) {
		if (javaProject == null) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, "javaProject is null", null);
		}
		if (isJCBPProjectNatureEnabled(javaProject)) {
			return new Status(IStatus.WARNING, Activator.PLUGIN_ID, IStatus.OK, NATURE_ID + " is already enabled", null);
		}
		try {
			IProjectDescription description = javaProject.getProject().getDescription();
			String[] natures = description.getNatureIds();
			// add nature
			String[] newNatures = new String[natures.length + 1];
			System.arraycopy(natures, 0, newNatures, 0, natures.length);
			newNatures[natures.length] = JCBPCore.NATURE_ID;
			description.setNatureIds(newNatures);
			javaProject.getProject().setDescription(description, null);
			return new Status(IStatus.OK, Activator.PLUGIN_ID, IStatus.OK, NATURE_ID + " enabled successfully", null);
		} catch (Exception e) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, e.getMessage(), e);
		}
	}

	public static IStatus disableJCBPProjectNature(IJavaProject javaProject) {
		if (javaProject == null) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, "javaProject is null", null);
		}
		if (!isJCBPProjectNatureEnabled(javaProject)) {
			return new Status(IStatus.WARNING, Activator.PLUGIN_ID, IStatus.OK, NATURE_ID + " is already disabled", null);
		}
		try {
			IProjectDescription description = javaProject.getProject().getDescription();
			String[] natures = description.getNatureIds();
			// Remove the nature
			for (int i = 0; i < natures.length; ++i) {
				if (NATURE_ID.equals(natures[i])) {
					String[] newNatures = new String[natures.length - 1];
					System.arraycopy(natures, 0, newNatures, 0, i);
					System.arraycopy(natures, i + 1, newNatures, i, natures.length - i - 1);
					description.setNatureIds(newNatures);
					javaProject.getProject().setDescription(description, null);
					javaProject.getProject().deleteMarkers(JCBPCore.MARKER_TYPE, true, IResource.DEPTH_INFINITE);
					break;
				}
			}
			return new Status(IStatus.OK, Activator.PLUGIN_ID, IStatus.OK, NATURE_ID + " disabled successfully", null);
		} catch (Exception e) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, e.getMessage(), e);
		}
	}
}
